package com.tuzhi.common.structure;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 环形存储结构
 * @param <T>
 */
public class LoopLink<T> {

    Set<Node> set = new HashSet<>();

    Node current;

    /**
     * 获取当前数据
     * @return
     */
    public T peek() {
        if (current != null) {
            return (T) current.getT();
        }
        return null;
    }

    /**
     * 获取前一个数据
     * @return
     */
    public T peekPre() {
        if (current != null) {
            return (T) current.pre.getT();
        }
        return null;
    }

    /**
     * 获取后一个数据
     * @return
     */
    public T peekNext() {
        if (current != null) {
            return (T) current.next.getT();
        }
        return null;
    }

    /**
     * 取出当前的，并把当前设置为next
     * @return
     */
    public T pop() {
        if (current == null) {
            return null;
        }

        Node tmp = current;
        current = current.next;
        return (T) tmp.getT();
    }

    public boolean remove(T t) {
        if (!contains(t)) {
            return false;
        }
        Node tmp = find(new Node(t), current, new BigDecimal("1"));
        if (tmp == null) {
            return false;
        }

        tmp.pre.next = tmp.next;
        tmp.next.pre = tmp.pre;
        set.remove(tmp);

//        if (tmp.equals(current)) {
        if (equalsNode(tmp, current)) {
            current = tmp.next;
        }
        return true;
    }


    /**
     * 指定当前对象
     * @param t
     * @return
     */
    public boolean setCurrent(T t) {
        if (!contains(t)) {
            return false;
        }
        Node node = new Node(t);
        current = find(node, current, new BigDecimal("1"));
        return true;
    }

    /**
     * 添加List，并指到list最后节点
     * @param list
     */
    public void putList(List<T> list) {
        for (T t: list) {
            put(t);
            next();
        }
    }

    /**
     * 在当前节点后面添加新节点
     * @param t
     * @return
     */
    public boolean put(T t) {
        if (t == null) {
            return false;
        }
        if (contains(t)){
            return false;
        }

        Node node = new Node(t);
        set.add(node);
        if (set.size() == 1) {
            current = node;
            current.pre = node;
            current.next = node;
            return true;
        } else if (set.size() == 2) {
            current.next = node;
            current.pre = node;
            node.pre = current;
            node.next = current;
            return true;
        }

        Node n3 = current.next;
        n3.pre = node;

        current.next = node;

        node.next = n3;
        node.pre = current;

        return true;

    }

    /**
     * 把next节点设置为当前节点，并返回
     * @return
     */
    public T next() {
        current = current.next;
        return (T) current.getT();
    }


    /**
     * 以当前节点为起点返回所有
     * @return
     */
    public List<T> all() {
        List<T> list = new ArrayList<>();
        for (int i = 0; i < set.size(); i++) {
            list.add((T) current.getT());
            next();
        }
        return list;
    }

    /**
     * 递归查找节点
     * @param node
     * @param i
     * @return
     */
    private Node find(Node node, Node current, BigDecimal i) {
        if (i.intValue() > set.size()) {
            return null;
        }
        i = i.add(new BigDecimal("1"));
        if (equalsNode(node ,current)) {
            return current;
        } else {
            return find(node, current.next, i);
        }
    }

    private boolean equalsNode(Node node1, Node node2) {
        if (node1.getT().equals(node2.getT())) {
            return true;
        }
        return false;
    }

    /**
     * 判断set中是否包含用户数据
     * @param t
     * @return
     */
    private boolean contains(T t) {
        for (Node tmp : set) {
            if (tmp.getT().equals(t)) {
                return true;
            }
        }
        return false;
    }

    public class Node<T> {
        Node pre;
        Node next;

        T t;

        public Node(T t) {
            this.t = t;
        }

        public T getT() {
            return t;
        }

        public Node getPre() {
            return pre;
        }

        public void setPre(Node pre) {
            this.pre = pre;
        }

        public Node getNext() {
            return next;
        }

        public void setNext(Node next) {
            this.next = next;
        }
    }

}
